package com.cjlgb.design.common.security.cfgbean;

import com.cjlgb.design.common.core.bean.ResultBean;
import com.cjlgb.design.common.core.constant.HttpStatus;
import com.cjlgb.design.common.security.annotation.Inner;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.entity.ContentType;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.util.Optional;

/**
 * @author WFT
 * @date 2019/7/11
 * description: 资源服务配置
 */
@Slf4j
@RequiredArgsConstructor
public class ResourceServerConfiguration implements ResourceServerConfigurer {

    private final ObjectMapper objectMapper;
    private final ApplicationContext applicationContext;

    @SneakyThrows
    private void writeOut(HttpServletResponse response,int status,String msg){
        response.setStatus(status);
        response.setContentType(ContentType.APPLICATION_JSON.getMimeType());
        response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
        response.getWriter().append(this.objectMapper.writeValueAsString(ResultBean.build(status,msg)));
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources){
        //  验证入口点
        resources.authenticationEntryPoint(
                (request,response,cause) ->
                        this.writeOut(response,HttpStatus.UNAUTHORIZED,cause.getMessage())
        );
        //  请求被拒绝访问时使用改处理器
        resources.accessDeniedHandler(
                (request,response,cause) ->
                        this.writeOut(response,HttpStatus.FORBIDDEN,cause.getMessage())
        );
    }

    /**
     * 匹配字符串中字符的正则表达式
     * 例如：{id},{name}
     */
    private static final String REGULAR = "\\{(.*?)}";

    @Override
    @SneakyThrows
    public void configure(HttpSecurity http){
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();
        //  得到请求与处理程序的映射
        RequestMappingHandlerMapping handlerMapping = this.applicationContext.getBean(RequestMappingHandlerMapping.class);
        handlerMapping.getHandlerMethods().forEach((mappingInfo,handlerMethod) -> {
            //  获取处理程序上的Inner注解
            Inner inner = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Inner.class);
            //  如果注解不为空,则获取方法上的路径,并设置不需要进行鉴权校验
            Optional.ofNullable(inner)
                    .ifPresent(bean -> mappingInfo.getPatternsCondition()
                            .getPatterns()
                            .forEach(uri ->
                                    registry.antMatchers(uri.replaceAll(REGULAR,"*")).permitAll()));
        });
        //  其他路径都需要鉴权校验
        registry.anyRequest().authenticated();
        http.csrf().disable();
    }

}
